#ifndef SERVEUR_H_
#define SERVEUR_H_

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <memory.h>
#include <errno.h>
#include <fstream>
#include <sstream>
#include <string>
#include <list>

#include "threads.h"
#include "partie.h"

using namespace std;

/*!
 * \class Serveur singleton
 * \brief Serveur multiport principal du jeux.
 * \date 2007
 * 
 * Serveur principal du jeu. Ne peut être instancié qu'une seule fois.
 * Hérite de la classe Thread.
 * Gere les differents serveurs de partie
 * 
 */
class Serveur : public Threads {
	public:

	/*!
	 * \brief Fonction permettant de récupérer l'instance unique de la classe Serveur.
	 * \param portServeurPartie Port d'attente des serveurs de partie.
	 * \param portLecture Port d'attente des clients.
	 * \param portEcouteClientServeurPartie Port d'ecoute des clients des serveurs de partie.
	 * \param portLectureServeurPartie Port de lecture des messages des serveurs de partie.
	 * \param portEcouteServeurPartie Port d'ecoute du serveur de partie distant.
	 * \return Le singleton serveur de partie.
	 *
	 */ 
	static Serveur * getInstance (u_short portServeurPartie, u_short portLecture,
			u_short portEcouteClientServeurPartie, u_short portLectureServeurPartie, u_short portEcouteServeurPartie);

	/*!
	 * \brief Fait pointer le singleton sur NULL.
	 *
	 *  Pour une future utilisation possible de la classe.
	 *
	 */
	static void kill ();








				//
				// Méthodes relatives à la création de threads.
				// Concrétise les virtuelles pures de la classe Threads.
				//


	/*!
	 *
	 * \brief Lance le thread d'écoute de nouveaux serveurs de partie.
	 *
	 */
	virtual void traitementEcouteServeurPartie() {
		this->connectionServeurPartie();
	}
	/*!
	 *
	 * \brief Lance le thread d'écoute de traitements des serveurs de partie.
	 *
	 */
	virtual void traitementNouveauServeurPartie() {
		this->traiterNouveauServeurPartie();
	}


	/*!
	 *
	 * \brief Lance le thread d'écoute de nouveaux clients.
	 *
	 */
	virtual void traitementEcouteClient() {
		this->connectionClient();
	}
	/*!
	 *
	 * \brief Lance le thread d'écoute de traitements des clients.
	 *
	 */
	virtual void traitementClient() {
		this->traiterClient();
	}


	/*!
	 *
	 * \brief Lance le thread de lecture des serveurs de partie.
	 *
	 */
	virtual void traitementLectureServeurPartie() {
		this->lectureServeurPartie();
	}
	/*!
	 *
	 * \brief Lance le thread de traitement des serveurs de partie.
	 *
	 */
	virtual void traitementServeurPartie() {
		this->traiterServeurPartie();
	}


	/*!
	 *
	 * \brief Lance le thread de test de présence des serveurs de partiee.
	 *
	 */
	virtual void traitementTestServeurPartie() {
		this->testPresenceServeur();
	}


	/*!
	 *
	 * \brief Lance le thread de renouvellement du jeton.
	 *
	 */
	virtual void traitementJeton() {
		this->sendJeton();
	}








				//
				// Méthodes lancées dans des threads.
				//



	/*!
	 *
	 * \brief Attend la connection d'un serveur de partie.
	 * 
	 * Cette méthode est lancée dans un thread.
	 * 
	 */
	int * connectionServeurPartie();
	
	/*!
	 * 
	 * \brief Attend la connexion d'un client.
	 * 
	 * Cette méthode est lancée dans un thread.
	 * 
	 */
	int * connectionClient();


	/*!
	 *
	 * \brief Traite la connection d'un serveur de partie.
	 *
	 * Cette méthode est lancée dans un thread.
	 *
	 */
	void * traiterNouveauServeurPartie();

	/*!
	 *
	 * \brief Traite les messages des serveurs de partie.
	 *
	 * Cette méthode est lancée dans un thread.
	 *
	 */
	void * traiterServeurPartie();

	/*!
	 * 
	 * Traite la connection d'un client.
	 *
	 * Cette méthode est lancée dans un thread.
	 *
	 */	
	void * traiterClient();

	/*!
	 *
	 * \brief Attend les messages des serveurs de partie.
	 *
	 * Cette méthode est lancée dans un thread.
	 *
	 */
	int * lectureServeurPartie();
	

	/*!
	 * 
	 * \brief Test si un serveur de partie n'a pas planté.
	 *
	 * Cette méthode est lancée dans un thread.
	 *
	 */
	void * testPresenceServeur();

	/*!
	 * 
	 * \brief Envoie le nouveau jeton aux serveurs de partie.
	 *
	 * Cette méthode est lancée dans un thread.
	 *
	 */
	void * sendJeton();








				//
				// Méthodes relatives à la gestion des utilisateurs
				// Identification, ajout/suppression de joueurs, etc.
				//


	/*!
	 *
	 * \brief Recherche un utilisateur.
	 * \param login Le login de l'utilisateur rechercher.
	 * \return Le code de l'utilisateur s'il existe, la chaîne vide sinon.
	 *
	 */
	string seekUser(string login);

	/*!
	 *
	 * \brief Enregistre un utilisateur dans le ficher.
	 * \param login Le login de l'utilisateur à créer.
	 * \param code Le code de l'utilisateur à créer.
	 *
	 */
	void registerUser(string login, string code);
	
	/*!
	 *
	 * \brief Supprime l'utilisateur de la liste des joueurs.
	 * \param login Le login de l'utilisateur à désinscrire.
	 *
	 */
	void unRegisterUser(string login);
	
	/*!
	 *
	 * \brief Demande aux serveurs de partie des renseignements sur leur états (nombre de joueurs, ports, etc.).
	 *
	 */
	void * infoGame();
	
	/*!
	 *
	 * \brief Ajoute un serveur de partie à la liste des jeux.
	 * \param partie Un objet Partie représentant la nouvelle partie.
	 * \return Un entier indiquant si l'ajout s'est bien passé.
	 *
	 */
	int addGame(Partie partie);

	/*!
	 *
	 * \brief Supprime un serveur de partie à la liste des jeux.
	 * \param adresse L'adresse du joueur à supprimer de la partie.
	 * \return Un entier indiquant si la suppression s'est bien passée.
	 *
	 */	
	int delGame(string adresse);
	
	/*!
	 *
	 * \brief Renvoie le nombre de partie en cours.
	 * \return Le nombre de partie.
	 *
	 */
	int getNumberOfGame();
	
	/*!
	 *
	 * \brief Écrit dans un fichier de log les événements particuliers d'une partie.
	 * \param message Le message a logguer.
	 *
	 * Écrit à la fin du fichier sans écraser les données déjà présentes.
	 *
	 */
	void log(string message);








				//
				// Socket.
				//


	/*!
	 *
	 * \brief Crée simplement un socket.
	 * \param port Le port du socket.
	 * \return Un entier indiquand si la création s'est bien passée.
	 *
	 */
	int createSock(u_short port);
	
	/*!
	 *
	 * \brief Ferme un socket.
	 * \param sock Le socket à fermer.
	 *
	 */
	void close_serveur(int sock);








	private:
	/*!
	 *
	 * \brief Constructeur du serveur principal.
	 * \param portServeurPartie Port d'attente des serveurs de partie.
	 * \param portLecture Port d'attente des clients.
	 * \param portEcouteClientServeurPartie Port d'ecoute des clients des serveurs de partie.
	 * \param portLectureServeurPartie Port de lecture des messages des serveurs de partie.
	 * \param portEcouteServeurPartie Port d'ecoute du serveur de partie distant.
	 *
	 * On empêche la création directe d'objets Singleton en mettant son constructeur par défaut privé.
	 * Ainsi on oblige l'utilisateur à passer par GetInstance.
	 *
	 */
	Serveur(u_short portServeurPartie, u_short portLecture, u_short portLectureServeurPartie,
			u_short portEcouteClientServeurPartie, u_short portEcouteServeurPartie);

	/*!
	 *
	 * \brief Destructeur.
	 *
	 * On empêche la destruction directe d'objets Singleton en mettant son destructeur par défaut privé.
	 *
	 */
	~Serveur();

	/*!
	 *
	 * \brief On empêche la recopie d'objets Singleton en mettant le constructeur par copie en privé.
	 *
	 * On peut même ne pas l'implémenter, c'est encore mieux.
	 *
	 */
	Serveur(const Serveur&);
	
	/*!
	 *
	 * \brief On empêche l'affectation d'objets Singleton en mettant l'opérateur d'affectation en privé.
	 *
	 * On peut même ne pas l'implémenter, c'est encore mieux.
	 *
	 */
  	const Serveur& operator=(const Serveur&);


	static Serveur * _singleton; /*!< Objet singleton Serveur. */




	list<Partie> gameList; /*!< Liste des parties. */

	int numberOfGame; /*!< Nombre de partie. */
	int maxGame; /*!< Nombre de partie maximales. */

	u_short portServeurPartie; /*!< Port d'écoute de nouveaux serveurs de parties. */
	u_short portClient; /*!< Port d'écoute de nouveaux clients. */

	u_short portEcouteServeurPartie; /*!< Port d'écoute du serveur de partie distant */
	u_short portEcouteClientServeurPartie; /*!< Port d'écoute des nouveaux clients du serveur de partie distant */

	u_short portLectureServeurPartie; /*!< Port d'écoute du serveur. */
	u_short portEcriture; /*!< Port d'écriture du serveur. */

	int sockServeurPartie;
	int sockClient;
	int sockLectureServeurPartie; 
	int sockEcriture;
	int msgsock;
	int msg_sockServeurPartie;
	int msg_sockClient;
	int msg_sockLectureServeurPartie;
};

#endif /*SERVEUR_H_*/
